// nettest.cc 
//	Test out message delivery between two "Nachos" machines,
//	using the Post Office to coordinate delivery.
//
//	Two caveats:
//	  1. Two copies of Nachos must be running, with machine ID's 0 and 1:
//		./nachos -m 0 -o 1 &
//		./nachos -m 1 -o 0 &
//
//	  2. You need an implementation of condition variables,
//	     which is *not* provided as part of the baseline threads 
//	     implementation.  The Post Office won't work without
//	     a correct implementation of condition variables.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.



//		on the first nachos as a server,  input "nachos -m 0 -ser";			
//		on the second nachos as a client, input "nachos -x ../test/difProcessLockTest -m 1"
//

					

#include "copyright.h"
#include "system.h"
#include "network.h"
#include "post.h"
#include "interrupt.h"
#include "sys/time.h"
// Test out message delivery, by doing the following:
//	1. send a message to the machine with ID "farAddr", at mail box #0
//	2. wait for the other machine's message to arrive (in our mailbox #0)
//	3. send an acknowledgment for the other machine's message
//	4. wait for an acknowledgement from the other machine to our 
//	    original message
#define SERVERMSGBOX 1
#define CLIENTMSGBOX 0
// Sever receive message from server through mail box 1
// Server get message from client and forward message to server through mail box 0


void
MailTest(int farAddr)
{
    PacketHeader outPktHdr, inPktHdr;
    MailHeader outMailHdr, inMailHdr;
    char *data = "Hello there!";
    char *ack = "Got it!";
    char buffer[MaxMailSize];

    // construct packet, mail header for original message
    // To: destination machine, mailbox 0
    // From: our machine, reply to: mailbox 1
    outPktHdr.to = farAddr;		
    outMailHdr.to = 0;
    outMailHdr.from = 1;
    outMailHdr.length = strlen(data) + 1;

    // Send the first message
    bool success = postOffice->Send(outPktHdr, outMailHdr, data); 

    if ( !success ) {
      printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }

    // Wait for the first message from the other machine
    postOffice->Receive(0, &inPktHdr, &inMailHdr, buffer);
    printf("Got \"%s\" from %d, box %d\n",buffer,inPktHdr.from,inMailHdr.from);
    fflush(stdout);

    // Send acknowledgement to the other machine (using "reply to" mailbox
    // in the message that just arrived
    outPktHdr.to = inPktHdr.from;
    outMailHdr.to = inMailHdr.from;
    outMailHdr.length = strlen(ack) + 1;
    success = postOffice->Send(outPktHdr, outMailHdr, ack); 

    if ( !success ) {
      printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }

    // Wait for the ack from the other machine to the first message we sent.
    postOffice->Receive(1, &inPktHdr, &inMailHdr, buffer);
    printf("Got \"%s\" from %d, box %d\n",buffer,inPktHdr.from,inMailHdr.from);
    fflush(stdout);

    // Then we're done!
    interrupt->Halt();
}


int CreateServerLock(char* lockName) {
	int index = -1;
	for (int i=0; i<MAXLOCKNUM; i++ ) {		

		if((ServerLockTable[i].theServerLock!=NULL) && (!strcmp(lockName,ServerLockTable[i].theServerLock->serverLockName))){ 	//Find the name is already there
			//Find the name is already there
			printf("the lock already exists\n");
			return i;
		}	
	}
	
	//The serverLock does not exist, make it!	
	for (int i=0; i<MAXLOCKNUM; i++ ){
		if(ServerLockTable[i].theServerLock == NULL) {
			index = i;
			break;
		}		
	}
	
	if(index == -1) {
		printf("CreateServerLock Error: No more ServerLock is avaible\n");
		return -1;
	}
	printf("DEBUG: the lock name is %s\n",lockName);
	ServerLockTable[index].theServerLock = new ServerLock(lockName);
	ServerLockTable[index].inUse = false;
	
	return index;
       
}

int DestroyServerLock(int index) {

	if(ServerLockTable[index].theServerLock == NULL){
		printf("rpcdebug:DestroyServerLock Error: The server lock has been destroyed or not existed!\n");
		return NOTOK;
	}
	if(ServerLockTable[index].inUse == true){
		printf("Fatal Destroy ServerLock Error: The server lock you are trying to destroy is in USE!\n");
		return NOTOK;
	}
	
	delete ServerLockTable[index].theServerLock;
	ServerLockTable[index].theServerLock = NULL;
	ServerLockTable[index].inUse = false;
	return OK;
}

int AcquireServerLock(int index, struct Socket* subSocket) {
		
	if(ServerLockTable[index].theServerLock == NULL){
		printf("AquireServerLock Error: The server lock has been destroyed or not existed!\n");
		return NOTOK;
	}
	int status;
	status = ServerLockTable[index].theServerLock->ServerLockAcquire(subSocket);  //status = 1, acquire successful; status = 0, wait in the queue
	ServerLockTable[index].inUse = true;
	return status;
	
}

struct Socket* ReleaseServerLock(int index, struct Socket* subSocket) {
	if(ServerLockTable[index].theServerLock == NULL){
		printf("ReleaseServerLock Error: The server lock has been destroyed or not existed!\n");
		return NULL;
	}

	struct Socket* returnSocket = ServerLockTable[index].theServerLock->ServerLockRelease(subSocket);
	if(ServerLockTable[index].theServerLock->serverLockStateBusy == false){ //No one wait for this lock
		ServerLockTable[index].inUse = false;
	}

	return returnSocket;
}


int CreateServerCondition(char* cvName) {
	int index = -1;
	for (int i=0; i<MAXCVNUM; i++ ) {
		if(ServerContitionTable[i].theServerCondition!=NULL && (!strcmp(cvName,ServerContitionTable[i].theServerCondition->serverConditionName))){ 
		//Find the name is already there
		printf("rpcDebug:the CV name already exists\n");
				return i;
		}	
	}
	
	//The serverCondition does not exist, make it!	
	for (int i=0; i<MAXCVNUM; i++ ){
		if(ServerContitionTable[i].theServerCondition == NULL) {
			index = i;
			break;
		}		
	}
	
	if(index == -1) {
		printf("CreateServerCondition Error: No more ServerCondition is avaible\n");
		return -1;
	}
	printf("DEBUG: the cv name is %s\n",cvName);
	ServerContitionTable[index].theServerCondition = new ServerCondition(cvName);
	ServerContitionTable[index].numOfWait = 0;
	
	return index;
}

int DestroyServerCondition(int index) {

	if(ServerContitionTable[index].theServerCondition == NULL){
		printf("rpcDebug:DestroyServerCondition Error: The server condition has been destroyed or not existed!\n");
		return NOTOK;
	}

	if(ServerContitionTable[index].numOfWait > 0) {
		printf("Fatal DestroyCondition Error: The condition you are trying to destroy is in USE!\n");
		return NOTOK;
	
	}
	
	delete ServerContitionTable[index].theServerCondition;
	ServerContitionTable[index].theServerCondition = NULL;
	ServerContitionTable[index].numOfWait = 0;
	
	return OK;

}

struct Socket* WaitServerCondition(int lockIndex, int cvIndex, struct Socket* subjectSocket) {
	
	if(ServerLockTable[lockIndex].theServerLock == NULL){
		printf("WaitServerCondition Error: The server lock has been destroyed!\n");
		return NULL;
	}
	
	if(ServerContitionTable[cvIndex].theServerCondition == NULL){
		printf("WaitServerCondition Error: The server condition has been destroyed!\n");
		return NULL;
	}
	
	ServerContitionTable[cvIndex].numOfWait++;
	struct Socket* releasedSocket = ServerContitionTable[cvIndex].theServerCondition->ServerConditionWait(lockIndex, subjectSocket);

	return releasedSocket;

}

struct Socket* SignalServerCondition(int lockIndex, int cvIndex, struct Socket* subjectSocket) {
	if(ServerLockTable[lockIndex].theServerLock == NULL){
		printf("SignalServerCondition Error: The server lock has been destroyed!\n");
		return NULL;
	}
	
	if(ServerContitionTable[cvIndex].theServerCondition == NULL){
		printf("SignalServerCondition Error: The server condition has been destroyed!\n");
		return NULL;
	}
	
	struct Socket* wakedSocket = ServerContitionTable[cvIndex].theServerCondition->ServerConditionSignal(lockIndex, subjectSocket);

	if(wakedSocket!=NULL) {
		ServerContitionTable[cvIndex].numOfWait--;
	}

	return wakedSocket;
}

bool isValidSignal(int lockIndex, int cvIndex, struct Socket* subjectSocket){
				//check lock index not -1
				//lock held by current owner
				//cv->serverLock index not -1
				//cv->serverLock current waiting lock
	if(lockIndex == -1) return false;
	if(cvIndex == -1) return false;
	if(subjectSocket == NULL) return false;
	if(ServerContitionTable[cvIndex].theServerCondition == NULL) return false;
	if(ServerLockTable[lockIndex].theServerLock == NULL) return false;
	if(ServerLockTable[lockIndex].theServerLock->isHeldByCurrentSocket(subjectSocket) == false) return false;
	if(ServerContitionTable[cvIndex].theServerCondition->getServerLockIndex()!= lockIndex)	return false;
	
	return true;
}

int BroadcastServerCondition(int lockIndex, int cvIndex, struct Socket* subjectSocket, bool reply) {
	int status = OK;
	if(isValidSignal(lockIndex,cvIndex,subjectSocket) == false) return NOTOK;
	if(ServerLockTable[lockIndex].theServerLock == NULL){
		printf("BroadcastServerCondition Error: The server lock has been destroyed!\n");
		return NOTOK;
	}
	
	if(ServerContitionTable[cvIndex].theServerCondition == NULL){
		printf("BroadcastServerCondition Error: The server condition has been destroyed!\n");
		return NOTOK;
	}
	if(reply == true){
		status = ServerContitionTable[cvIndex].theServerCondition->ServerConditionBroadcast(lockIndex, subjectSocket);
		if(status == OK) {
			ServerContitionTable[cvIndex].numOfWait = 0;
		}
	}else{
		while(ServerContitionTable[cvIndex].theServerCondition->getServerLockIndex() != -1){
			ServerContitionTable[cvIndex].theServerCondition->ServerConditionSignal(lockIndex,subjectSocket);
			ServerContitionTable[cvIndex].numOfWait--;
		}
	
	}
	
	return status;
}


int CreateServerMV(char* name, int arrayNum, int iniValue){
	int index = -1;
	for (int i=0; i<MAXMVNUM; i++ ) {
		if(MVTable[i].mvArray!=NULL && (!strcmp(name,MVTable[i].mvName))){ 
		//Find the name is already there
		printf("rpcDebug:The MV name already exists\n");
				return i;
		}	
	}
	
	//The MV does not exist, make it!	
	for (int i=0; i<MAXMVNUM; i++ ){
		if(MVTable[i].mvArray == NULL) {
			index = i;
			break;
		}		
	}
	
	if(index == -1) {
		printf("CreateServerMV Error: No more ServerMV is avaible\n");
		return -1;
	}
	
	MVTable[index].mvArray = new int[arrayNum];
	for(int i=0; i<arrayNum; i++) {
		MVTable[index].mvArray[i] = iniValue;
	}
	
	if(MVTable[index].mvName==NULL) {
		MVTable[index].mvName=new char[MAXSTRINGLENGTH];
	}
	strcpy(MVTable[index].mvName, name);
	MVTable[index].arraySize = arrayNum;
	printf("DEBUG: the mv name is %s\n",name);
	return index;
	
} 

int DestroyServerMV(int index) {
	if(MVTable[index].mvArray == NULL){
		printf("rpcDebug:DestroyServerMV Error: The server mv has been destroyed or not existed!\n");
		return NOTOK;
	}
	
	delete[] MVTable[index].mvArray;
	MVTable[index].mvArray = NULL;
	MVTable[index].mvName = NULL;
	MVTable[index].arraySize = 0;
	return OK;

}

int GetValueMV(int mvIndex, int arrayIndex){
	int value = -1;
	if(MVTable[mvIndex].mvArray == NULL){
		printf("rpcDebug:GetValueMV Error: The server mv has been destroyed or not existed!\n");
		return WRONGVALUE;
	}
	
	if(arrayIndex >= MVTable[mvIndex].arraySize) {
		printf("GetValueMV Error: The server mv array with %d elements do not have the element %d you want!\n",MVTable[mvIndex].arraySize,arrayIndex);
		return WRONGVALUE;
	}
	
	value = MVTable[mvIndex].mvArray[arrayIndex];
	return value;
	

}

int SetValueMV(int mvIndex, int arrayIndex, int value){

	if(MVTable[mvIndex].mvArray == NULL){
		printf("rpcDebug:SetValueMV Error: The server mv has been destroyed or not existed!\n");
		return NOTOK;
	}
	if(arrayIndex >= MVTable[mvIndex].arraySize) {
		printf("SetValueMV Error: The server mv array with %d elements do not have the element %d you want!\n",MVTable[mvIndex].arraySize,arrayIndex);
		return NOTOK;
	}
	
	MVTable[mvIndex].mvArray[arrayIndex] = value;
	return OK;
}


void delay(	int r){
	int i;
	for(i=0;i<r;i++){
		currentThread->Yield();	
	}
}
void checkInActClient(){
	//printf("tangDebug: I am going to check the Inactive Client\n");

	time_t time_bg = time(NULL);
	struct timeval beginTime;
	if(gettimeofday(&beginTime,NULL) != 0){
			printf("\nServer: Error in gettimeofday()");
			interrupt->Halt();
	}
	
	// go through all the lock owner

	while(true){
		for(int i=0;i<MAXLOCKNUM;i++){
			// if there is a lock and the lock owner died 
			//printf("tangDebug: In server mailbox 2:: I am checking lock %d\n",i);
			if((ServerLockTable[i].theServerLock != NULL)&&(ServerLockTable[i].theServerLock->serverLockStateBusy == true)){
				//get the lock owner
				Socket *owner = ServerLockTable[i].theServerLock->getSeverLockOwner();
				//check whether it's dead
				PacketHeader outPktHdr;
				MailHeader outMailHdr;
				RpcReqMessage tmpMsg;
				outPktHdr.to = owner->machineID;
				outMailHdr.to = 1;
				char * sendData = (char *) (&tmpMsg);
				outMailHdr.length = sizeof(tmpMsg);
				//printf("tangDebug: In server mailbox 2:: I am gonna to send to %d\n",owner->machineID);
				int suc = postOffice->Send(outPktHdr, outMailHdr, sendData);
				
				//if it's dead
				if(suc != 1){
					printf("Server %d detected client %d is inactive.\n",machineID, owner->machineID);
					//printf("tangDebug: In server mailbox 2:: client %d is inActive", owner->machineID);
					//get valid timestamp
					struct timeval currentTime;
					time_t time_ct = time(NULL);
					int64_t time_stmp;
					if(gettimeofday(&currentTime,NULL) != 0){
						printf("\nServer: Error in gettimeofday()");
						interrupt->Halt();
					}
					time_stmp = ((int64_t)currentTime.tv_sec * 1000000) + (int64_t)currentTime.tv_usec;
					
					if((currentTime.tv_sec - beginTime.tv_sec)<(time_ct-time_bg)){
						printf("Server: gettimeofday reset at %d\n", (int)time_ct);
						time_stmp += ((int64_t)time_ct -(int64_t) time_bg) * 1000000;
					}
					
					//compose a fake message


					RpcFwdMessage fakeFwdMsg;
					RpcReqMessage fakeReqMsg;
							//fake request message			
					fakeReqMsg.reqType = RELEASELOCK;
					fakeReqMsg.intArgNum = 1;
					fakeReqMsg.intArg[0] = i;
							//fake forward message
					
					fakeFwdMsg.fwdType = REQUESTMSG;
					fakeFwdMsg.clientSock = *owner;
					fakeFwdMsg.serverSock.machineID = machineID;
					fakeFwdMsg.serverSock.mailboxNum = 2;
					fakeFwdMsg.timeStamp = time_stmp;
					fakeFwdMsg.reqMessage = fakeReqMsg;
					outMailHdr.from = SERVERMSGBOX;
					outMailHdr.from = CLIENTMSGBOX;
					outMailHdr.length = sizeof(fakeFwdMsg);
					sendData = (char *) (&fakeFwdMsg);
					//printf("tangDebug: In server mailbox 2:: Fake\n");
								
					//forward this to all the server
					for(int j=0;j<serverSize;j++){
						outPktHdr.to = j;
						int success = postOffice->Send(outPktHdr, outMailHdr, sendData);
						printf("Handling Inactive client %d ...\n", owner->machineID);
						//printf("\ntangDebug: In Server mailbox 2: fwd message to machine %d mailbox %d a client request from client machineid %d\n", outPktHdr.to, outMailHdr.to, machineID);
						if(!success){
							printf("ERROR: Server failure in postOffice->Send()");
							interrupt->Halt();
						}
					}
				}
				
			}//end of if the lock is not null
		}//end of for
		
		delay(1<<20);
	}//end of while
	
}

void HandleServerMsg(){

	
	if((machineID == 0)&&(bullyOn == true)){
		
		Thread *t = new Thread("checkInActClient");
		t->Fork((VoidFunctionPtr)checkInActClient,0);
	}
	//int serverSize = 3;
	int64_t ltrTable[serverSize];	//Last Time Receive Table
	List * pendReqList = new List();
	//initialize LTR table
	for(int i =0; i<serverSize; i++){
		ltrTable[i] = -1;
		//std::cout << "\n tangDebug: initializing int64_t ltr table entry "<<i<<" as "<<ltrTable[i];
	}
	
	time_t time_bg = time(NULL);
	struct timeval beginTime;
	if(gettimeofday(&beginTime,NULL) != 0){
		printf("\nServer: Error in gettimeofday()");
		interrupt->Halt();
	}
	
	int msgCounter = 0;
	int msgArraySize =  50;
	struct RpcFwdMessage tmpMsg[msgArraySize];	// To store the message in case of
												//newly received message will over write the old one
	while(true){
		//Receive Msg
		PacketHeader inPktHdr;
		MailHeader inMailHdr;
		char recvBuff[MaxMailSize];
		
		postOffice->Receive(SERVERMSGBOX, &inPktHdr, &inMailHdr, recvBuff);		
		fflush(stdout);
		
		//extract machineID and timeStamp
		RpcFwdMessage recvMessage = *((struct RpcFwdMessage *) recvBuff);
		int64_t time_recv = recvMessage.timeStamp;
		int machine_id = recvMessage.serverSock.machineID;
		//printf("\ntangDebug: in mailbox1:received message with type %d and machine id %d from server machine %d mailbox %d",recvMessage.fwdType, machine_id, inPktHdr.from, inMailHdr.from);
		//std::cout << "\ntangDebug: Mailbox 1:: Receive msg with type "<<recvMessage.fwdType<<" from machineID "<<machine_id<<" with strArg "<<recvMessage.reqMessage.strArg<<" with timeStamp " <<time_recv;
		//fflush(stdout);
		//if request msg, reply an timestamp message to the server unless it's myself
						//and add it into pending message queue
						

		if(recvMessage.fwdType == REQUESTMSG){
			// add message into pending request message queue
			int64_t key =  ((int64_t)time_recv * 10)+ ((int64_t)(machine_id));
			//std::cout <<"\n key is "<<key;
		
			msgCounter = (msgCounter + 1)% msgArraySize;
			tmpMsg[msgCounter] = recvMessage;
			pendReqList->SortedInsert((void *) &tmpMsg[msgCounter], key);	
			//std::cout<<"\ntangDebug: Mailbox 1:: Insert request message with key "<<key<<" with strArg "<<tmpMsg[msgCounter].reqMessage.strArg;
			//std::cout<<" with timeStamp "<<tmpMsg[msgCounter].timeStamp<<" at first received";
			//fflush(stdout);
			
			//if(machine_id != inPktHdr.to){
			//get valid timestamp
			struct timeval currentTime;
			time_t time_ct = time(NULL);
			int64_t time_stmp;
			if(gettimeofday(&currentTime,NULL) != 0){
				printf("\nServer: Error in gettimeofday()");
				interrupt->Halt();
			}
			time_stmp = ((int64_t)currentTime.tv_sec) * 1000000 + (int64_t)currentTime.tv_usec;
			if((currentTime.tv_sec - beginTime.tv_sec)<(time_ct-time_bg)){
				printf("\nServer: gettimeofday reset at %d", (int)time_ct);
				time_stmp += ((int64_t)time_ct - (int64_t)time_bg) * 1000000;
			}
			
			//prepare timestamp RpcFwdMessage
			struct RpcFwdMessage timeStmpMsg;			
			timeStmpMsg.fwdType = TIMESTAMPMSG;
			timeStmpMsg.timeStamp = time_stmp;
			timeStmpMsg.serverSock.machineID = inPktHdr.to;
			char * timeStmpBuf = (char *) &timeStmpMsg;

			//prepare Header
			PacketHeader outPktHdr_TS;
			MailHeader outMailHdr_TS;
			outPktHdr_TS.to = recvMessage.serverSock.machineID;
			outMailHdr_TS.to =  SERVERMSGBOX;
			
			outMailHdr_TS.length = sizeof(timeStmpMsg);
			
			//send the timestamp msg
			for(int i=0; i<serverSize; i++){
				outPktHdr_TS.to = i;
				
				int success = postOffice->Send(outPktHdr_TS, outMailHdr_TS, timeStmpBuf);
				if ( !success ) {
					printf("\nThe postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
					printf("\n In Server mailbox 1\n");
					interrupt->Halt();
				}
			}
			//printf("\ntangDebug: Mailbox 1:: Send a TimestampMessage to machine ID %d with timeStamp ", timeStmpMsg.serverSock.machineID);
			//std::cout <<timeStmpMsg.timeStamp;
			//fflush(stdout);
			//}
		}	// end of if request message
		
		//update received last time received table
		ltrTable[machine_id] = time_recv;
		//cout<<"\ntangDebug: Mailbox 1:: Update ltrTable entry "<<machine_id<<" with timeStamp "<<time_recv;
		fflush(stdout);
		//get least value in ltrTable
		int64_t leastValue = ltrTable[0];
		//std::cout <<"\ntangDebug: "<<"least Value initialized as "<<leastValue;
		for(int i=1;i<serverSize;i++){
			if(ltrTable[i] < leastValue) leastValue = ltrTable[i];
			//printf("\ntangDebug: comparing ltrtable[%d]",i);
		}
		
		//std::cout <<"\ntangDebug: leastValue "<<leastValue;
		
		//handle qualified msg(ealier than least value in last time received table)
		
		if(pendReqList->IsEmpty() == true) continue;
		
		int64_t tmpKey = -1; 
		int64_t * rmvKey = &tmpKey;	
		struct RpcFwdMessage * fwdReqMsg = (RpcFwdMessage *)pendReqList->SortedRemove(rmvKey);
		int64_t tmpTime = fwdReqMsg->timeStamp;
		
		//cout<<"\ntangDebug: Mailbox 1:: Pull message from server machine ID "<<fwdReqMsg->serverSock.machineID<<" with strArg "<< fwdReqMsg->reqMessage.strArg<<" timeStamp "<<tmpTime<<" with key"<<*rmvKey;
		//cout<<"\ntangDebug: Mailbox 1:: leastValue is "<<leastValue;
		fflush(stdout);
		while((fwdReqMsg != NULL) && (leastValue >= fwdReqMsg->timeStamp)){
		
			PacketHeader outPktHdr;
			MailHeader outMailHdr;					
			RpcReqMessage requestMessage;
			RpcRplyMessage replyMessage;
			//handle the qualify message
			char* sendData;
			struct Socket client = fwdReqMsg->clientSock;
			struct Socket server = fwdReqMsg->serverSock;
			requestMessage = fwdReqMsg->reqMessage;
			int type = requestMessage.reqType;
			int receiveMachineID = client.machineID;
			int receiveMailbox = client.mailboxNum;
			
			if(receivedSocketTable[receiveMachineID][receiveMailbox] == NULL) {
			receivedSocketTable[receiveMachineID][receiveMailbox] = new Socket();
		}
		
		outPktHdr.to = receiveMachineID;
		outMailHdr.to = receiveMailbox;
		receivedSocketTable[receiveMachineID][receiveMailbox]->machineID = receiveMachineID;
		receivedSocketTable[receiveMachineID][receiveMailbox]->mailboxNum = receiveMailbox;

		//printf("\ntangDebug: Mailbox 1:: Handling message from server machine ID %d with strArg %s", server.machineID, requestMessage.strArg);
		//cout<<" and timeStamp "<<fwdReqMsg->timeStamp;
		fflush(stdout);
		
		replyMessage.rplyType = type;
		
		switch (type) {
			default:
			printf("Fatal Error Server: unknown request type\n");
			break;
			
			case CREATELOCK:		
				replyMessage.retValue = CreateServerLock(requestMessage.strArg);
				if(replyMessage.retValue == -1) {
					replyMessage.status = NOTOK;  
				}
				else {
					replyMessage.status = OK;
				} 
				
				outMailHdr.length = sizeof(replyMessage);
				sendData = (char*)(&replyMessage);		 
				
				if(server.machineID == inPktHdr.to){
					postOffice->Send(outPktHdr, outMailHdr, sendData);	 //Send Back the reply	
					printf("\ntangDebug: send reply to machine %d of request type %d ", client.machineID,requestMessage.reqType );
					fflush(stdout);
				}	
			break;
			
			case DESTROYLOCK:	
				replyMessage.status = DestroyServerLock(requestMessage.intArg[0]);
				outMailHdr.length = sizeof(replyMessage);
				sendData = (char*)(&replyMessage);	
				if(server.machineID == inPktHdr.to){
					postOffice->Send(outPktHdr, outMailHdr, sendData);	 //Send Back the reply	
					printf("\ntangDebug: send reply to machine %d of request type %d", client.machineID,requestMessage.reqType);
					fflush(stdout);
				}
			break;
			
			case ACQUIRELOCK:
				printf("DEBUG: AcquireLock: the received machine ID is %d, mailbox number is %d\n",inPktHdr.from,inMailHdr.from);
		
				replyMessage.status = AcquireServerLock(requestMessage.intArg[0],receivedSocketTable[receiveMachineID][receiveMailbox]);
				printf("The Lock status is %d\n",replyMessage.status);
				if(replyMessage.status == NOTOK || replyMessage.status == IAMLOCKOWNER || replyMessage.status==SUCCAQUR) {//I need send reply
					outMailHdr.length = sizeof(replyMessage);
					sendData = (char*)(&replyMessage);	
					if(server.machineID == inPktHdr.to){
						postOffice->Send(outPktHdr, outMailHdr, sendData);	 //Send Back the reply	
						printf("\ntangDebug: send reply to machine %d of request type %d", client.machineID,requestMessage.reqType);
						fflush(stdout);
					}
				}

			break;
			
			case RELEASELOCK:
								//Reply of release lock
				int tmpLockIndex = requestMessage.intArg[0];
				replyMessage.rplyType = RELEASELOCK;
				outPktHdr.to = receiveMachineID;
				outMailHdr.to = receiveMailbox;
				if((ServerLockTable[tmpLockIndex].theServerLock != NULL)&&(ServerLockTable[tmpLockIndex].theServerLock->isHeldByCurrentSocket(receivedSocketTable[receiveMachineID][receiveMailbox]) == true))
					replyMessage.status = OK;
				else replyMessage.status = NOTOK;
				outMailHdr.length = sizeof(replyMessage);
				sendData = (char*)(&replyMessage);	
				if(server.machineID == inPktHdr.to){
					postOffice->Send(outPktHdr, outMailHdr, sendData);	 //Send Back the reply	
					printf("\ntangDebug: send reply to machine %d of request type %d", client.machineID,requestMessage.reqType);
					fflush(stdout);
				}
			
				struct Socket* returnSocket = ReleaseServerLock(requestMessage.intArg[0],receivedSocketTable[receiveMachineID][receiveMailbox]);
				if(returnSocket !=NULL)  {			//Wake up other's socket
					replyMessage.rplyType = ACQUIRELOCK;
					replyMessage.status = IWASRELEASE;
					outPktHdr.to = returnSocket->machineID;
					outMailHdr.to = returnSocket->mailboxNum;

					outMailHdr.length = sizeof(replyMessage);
					sendData = (char*)(&replyMessage);	
					if(server.machineID == inPktHdr.to){
						postOffice->Send(outPktHdr, outMailHdr, sendData);	 //Send Back the reply	
						printf("\ntangDebug: send reply to machine %d of request type %d", client.machineID,requestMessage.reqType);
						fflush(stdout);
					}
				}
				

				
			break;		
			
			case CREATECV:	
				replyMessage.retValue = CreateServerCondition(requestMessage.strArg);
				if(replyMessage.retValue == -1) {
					replyMessage.status = NOTOK;  
				}
				else {
					replyMessage.status = OK;
				} 
				outMailHdr.length = sizeof(replyMessage);
				sendData = (char*)(&replyMessage);
				if(server.machineID == inPktHdr.to){
					postOffice->Send(outPktHdr, outMailHdr, sendData);	 //Send Back the reply	
					printf("\ntangDebug: send reply to machine %d of request type %d", client.machineID,requestMessage.reqType);
					fflush(stdout);
				}				
			break;
			
			case DESTROYCV:	
				replyMessage.status = DestroyServerCondition(requestMessage.intArg[0]);
				outMailHdr.length = sizeof(replyMessage);
				sendData = (char*)(&replyMessage);	
				if(server.machineID == inPktHdr.to){
					postOffice->Send(outPktHdr, outMailHdr, sendData);	 //Send Back the reply	
					printf("\ntangDebug: send reply to machine %d of request type %d", client.machineID,requestMessage.reqType);
					fflush(stdout);
				}
				
			break;
			
			case WAITCV:
				struct Socket* releasedSocket = WaitServerCondition(requestMessage.intArg[0],requestMessage.intArg[1],receivedSocketTable[receiveMachineID][receiveMailbox]);
				if(releasedSocket !=NULL)  {			//Release other's socket
					replyMessage.rplyType = ACQUIRELOCK;
					replyMessage.status = IWASRELEASE;
					outPktHdr.to = releasedSocket->machineID;
					outMailHdr.to = releasedSocket->mailboxNum;

					outMailHdr.length = sizeof(replyMessage);
					sendData = (char*)(&replyMessage);	
					if(server.machineID == inPktHdr.to){
						postOffice->Send(outPktHdr, outMailHdr, sendData);	 //Send Back the reply	
						printf("\ntangDebug: send reply to machine %d of request type %d", client.machineID,requestMessage.reqType);
						fflush(stdout);
					}
				}
				
			break;
			case SIGNALCV:
						//Reply of signal CV
				replyMessage.rplyType = SIGNALCV;
				outPktHdr.to = receiveMachineID;
				outMailHdr.to = receiveMailbox;
				
				//check lock index not -1
				//lock held by current owner
				//cv->serverLock index not -1
				//cv->serverLock current waiting lock
				
				if(isValidSignal(requestMessage.intArg[0],requestMessage.intArg[1],receivedSocketTable[receiveMachineID][receiveMailbox]))
					replyMessage.status = OK;
				else replyMessage.status = NOTOK;
				outMailHdr.length = sizeof(replyMessage);
				sendData = (char*)(&replyMessage);	
								if(server.machineID == inPktHdr.to){
					postOffice->Send(outPktHdr, outMailHdr, sendData);	 //Send Back the reply	
					printf("\ntangDebug: send reply to machine %d of request type %d", client.machineID,requestMessage.reqType);
					fflush(stdout);
				}
			
				struct Socket* wakedSocket = SignalServerCondition(requestMessage.intArg[0],requestMessage.intArg[1],receivedSocketTable[receiveMachineID][receiveMailbox]);
				if(wakedSocket != NULL)  {			//Wake up other's socket
					replyMessage.rplyType = WAITCV;
					replyMessage.status = IWASWAKEDUP;
					outPktHdr.to = wakedSocket->machineID;
					outMailHdr.to = wakedSocket->mailboxNum;

					outMailHdr.length = sizeof(replyMessage);
					sendData = (char*)(&replyMessage);	
					if(server.machineID == inPktHdr.to){
						postOffice->Send(outPktHdr, outMailHdr, sendData);	 //Send Back the reply	
						printf("\ntangDebug: send reply to machine %d of request type %d", client.machineID,requestMessage.reqType);
						fflush(stdout);
					}
				}
			
				
			
			break;
			case BROADCASTCV:
				bool reply = (server.machineID == inPktHdr.to);
				replyMessage.status = BroadcastServerCondition(requestMessage.intArg[0],requestMessage.intArg[1],receivedSocketTable[receiveMachineID][receiveMailbox],reply);
				outMailHdr.length = sizeof(replyMessage);
				sendData = (char*)(&replyMessage);	
				if(server.machineID == inPktHdr.to){
					postOffice->Send(outPktHdr, outMailHdr, sendData);	 //Send Back the reply	
					printf("\ntangDebug: send reply to machine %d of request type %d", client.machineID,requestMessage.reqType);
					fflush(stdout);
				}		
			break;
			
			case CREATEMV:	
			replyMessage.retValue = CreateServerMV(requestMessage.strArg,requestMessage.intArg[0],requestMessage.intArg[1]); //Arg[0]=arraySize Arg[1]=initValue
			if(replyMessage.retValue == -1) {
				replyMessage.status = NOTOK;  
			}
			else {
				replyMessage.status = OK;
			} 
			
			outMailHdr.length = sizeof(replyMessage);
			sendData = (char*)(&replyMessage);		 
			if(server.machineID == inPktHdr.to){
				postOffice->Send(outPktHdr, outMailHdr, sendData);	 //Send Back the reply	
				printf("\ntangDebug: send reply to machine %d of request type %d", client.machineID,requestMessage.reqType);
				fflush(stdout);
			}
			
		break;
		
		case DESTROYMV:	
			replyMessage.status = DestroyServerMV(requestMessage.intArg[0]);
			outMailHdr.length = sizeof(replyMessage);
			sendData = (char*)(&replyMessage);	
			if(server.machineID == inPktHdr.to){
				postOffice->Send(outPktHdr, outMailHdr, sendData);	 //Send Back the reply	
				printf("\ntangDebug: send reply to machine %d of request type %d", client.machineID,requestMessage.reqType);
				fflush(stdout);
			}
		break;
		
		case GETVALUEMV:	
			replyMessage.retValue = GetValueMV(requestMessage.intArg[0],requestMessage.intArg[1]);
			
			if(replyMessage.retValue == WRONGVALUE) {
				replyMessage.status = NOTOK;  
			}
			else {
				replyMessage.status = OK;
			} 
			
			outMailHdr.length = sizeof(replyMessage);
			sendData = (char*)(&replyMessage);		 
			if(server.machineID == inPktHdr.to){
				postOffice->Send(outPktHdr, outMailHdr, sendData);	 //Send Back the reply	
				printf("\ntangDebug: send reply to machine %d of request type %d", client.machineID,requestMessage.reqType);
				fflush(stdout);
			}
					
		break;
		
		case SETVALUEMV:
			replyMessage.status = SetValueMV(requestMessage.intArg[0],requestMessage.intArg[1],requestMessage.intArg[2]);
			outMailHdr.length = sizeof(replyMessage);
			sendData = (char*)(&replyMessage);	
			if(server.machineID == inPktHdr.to){
				postOffice->Send(outPktHdr, outMailHdr, sendData);	 //Send Back the reply	
				printf("\ntangDebug: send reply to machine %d of request type %d", client.machineID,requestMessage.reqType);
				fflush(stdout);
			}
		break;
	
		}
			//pull out another message from the queue
			fwdReqMsg = (RpcFwdMessage *)pendReqList->SortedRemove(rmvKey);
		}
		
		if(fwdReqMsg != NULL) {
			pendReqList->SortedInsert((void*) fwdReqMsg, *rmvKey);
			//RpcReqMessage m = (fwdReqMsg->reqMessage);
			//cout<<"\ntangDebug: Mailbox1 Insert fwdReqMsg with strArg "<<m.strArg<<" and key "<<(*rmvKey)<<" with timeStamp "<<fwdReqMsg->timeStamp<<" back";
			fflush(stdout);
		}
		
		
	}
	

}

void ServerP1(){
	
	while (true) {
		PacketHeader outPktHdr, inPktHdr;
		MailHeader outMailHdr, inMailHdr;

		char recvBuff[MaxMailSize];
		char* sendData;
		
		
	//	receivedSocket = new Socket();
		
		RpcReqMessage requestMessage;
		RpcRplyMessage replyMessage;
		
		postOffice->Receive(SEVERMAILBOX, &inPktHdr, &inMailHdr, recvBuff);//receive the request
		//printf("myDEBUG: Server: Got \"%s\" from %d, box %d\n",recvBuff,inPktHdr.from,inMailHdr.from); 
		fflush(stdout);
		int receiveMachineID = inPktHdr.from;
		int receiveMailbox = inMailHdr.from;
		
		if(receivedSocketTable[receiveMachineID][receiveMailbox] == NULL) {
			receivedSocketTable[receiveMachineID][receiveMailbox] = new Socket();
		}
		
		
		
		outPktHdr.to = inPktHdr.from;
		outMailHdr.to = inMailHdr.from;
		receivedSocketTable[receiveMachineID][receiveMailbox]->machineID = inPktHdr.from;
		receivedSocketTable[receiveMachineID][receiveMailbox]->mailboxNum = inMailHdr.from;

		requestMessage = *((struct RpcReqMessage*) recvBuff);
		int type = requestMessage.reqType; 
		
		//printf("The type is %d\n", type);
		replyMessage.rplyType = type;
		
		switch (type) {
	    default:
		printf("Fatal Error Server: unknown request type\n");
		break;
		
	    case CREATELOCK:		
			replyMessage.retValue = CreateServerLock(requestMessage.strArg);
			if(replyMessage.retValue == -1) {
				replyMessage.status = NOTOK;  
			}
			else {
				replyMessage.status = OK;
			} 
			
			outMailHdr.length = sizeof(replyMessage);
			sendData = (char*)(&replyMessage);		 
			postOffice->Send(outPktHdr, outMailHdr, sendData);	 //Send Back the reply		
		break;
		
		case DESTROYLOCK:	
			replyMessage.status = DestroyServerLock(requestMessage.intArg[0]);
			outMailHdr.length = sizeof(replyMessage);
			sendData = (char*)(&replyMessage);	
			postOffice->Send(outPktHdr, outMailHdr, sendData);	 //Send Back the reply
		break;
		
		case ACQUIRELOCK:
		//	printf("DEBUG: AcquireLock: the received machine ID is %d, mailbox number is %d\n",inPktHdr.from,inMailHdr.from);
	
			replyMessage.status = AcquireServerLock(requestMessage.intArg[0],receivedSocketTable[receiveMachineID][receiveMailbox]);
		//	printf("The Lock status is %d\n",replyMessage.status);
			if(replyMessage.status == NOTOK || replyMessage.status == IAMLOCKOWNER || replyMessage.status==SUCCAQUR) {//I need send reply
				outMailHdr.length = sizeof(replyMessage);
				sendData = (char*)(&replyMessage);	
				postOffice->Send(outPktHdr, outMailHdr, sendData);	 //Send Back the reply
			}

		break;
		
		case RELEASELOCK:
			
			int tmpLockIndex = requestMessage.intArg[0];
			replyMessage.rplyType = RELEASELOCK;
			outPktHdr.to = receiveMachineID;
			outMailHdr.to = receiveMailbox;
			if((ServerLockTable[tmpLockIndex].theServerLock != NULL)&&(ServerLockTable[tmpLockIndex].theServerLock->isHeldByCurrentSocket(receivedSocketTable[receiveMachineID][receiveMailbox]) == true))
				replyMessage.status = OK;
			else replyMessage.status = NOTOK;
			outMailHdr.length = sizeof(replyMessage);
			sendData = (char*)(&replyMessage);
			postOffice->Send(outPktHdr, outMailHdr, sendData);
			struct Socket* returnSocket = ReleaseServerLock(requestMessage.intArg[0],receivedSocketTable[receiveMachineID][receiveMailbox]);
			if(returnSocket !=NULL)  {			//Wake up other's socket
				replyMessage.rplyType = ACQUIRELOCK;
				replyMessage.status = IWASRELEASE;
				outPktHdr.to = returnSocket->machineID;
				outMailHdr.to = returnSocket->mailboxNum;

				outMailHdr.length = sizeof(replyMessage);
				sendData = (char*)(&replyMessage);	
				postOffice->Send(outPktHdr, outMailHdr, sendData);	 //Send Back the reply
			}
			
			//Reply of release lock

		break;		

		case CREATECV:	
			replyMessage.retValue = CreateServerCondition(requestMessage.strArg);
			if(replyMessage.retValue == -1) {
				replyMessage.status = NOTOK;  
			}
			else {
				replyMessage.status = OK;
			} 
			outMailHdr.length = sizeof(replyMessage);
			sendData = (char*)(&replyMessage);		 
			postOffice->Send(outPktHdr, outMailHdr, sendData);	 //Send Back the reply				
		break;
		
		case DESTROYCV:	
			replyMessage.status = DestroyServerCondition(requestMessage.intArg[0]);
			outMailHdr.length = sizeof(replyMessage);
			sendData = (char*)(&replyMessage);	
			postOffice->Send(outPktHdr, outMailHdr, sendData);	 //Send Back the reply
			
		break;
		
		case WAITCV:
			struct Socket* releasedSocket = WaitServerCondition(requestMessage.intArg[0],requestMessage.intArg[1],receivedSocketTable[receiveMachineID][receiveMailbox]);
			if(releasedSocket !=NULL)  {			//Release other's socket
				replyMessage.rplyType = ACQUIRELOCK;
				replyMessage.status = IWASRELEASE;
				outPktHdr.to = releasedSocket->machineID;
				outMailHdr.to = releasedSocket->mailboxNum;

				outMailHdr.length = sizeof(replyMessage);
				sendData = (char*)(&replyMessage);	
				postOffice->Send(outPktHdr, outMailHdr, sendData);	 //Send Back the reply
			}
			
		break;
		case SIGNALCV:
					//Reply of signal CV
				replyMessage.rplyType = SIGNALCV;
				outPktHdr.to = receiveMachineID;
				outMailHdr.to = receiveMailbox;
								
				if(isValidSignal(requestMessage.intArg[0],requestMessage.intArg[1],receivedSocketTable[receiveMachineID][receiveMailbox]))
					replyMessage.status = OK;
				else replyMessage.status = NOTOK;
				outMailHdr.length = sizeof(replyMessage);
				
			postOffice->Send(outPktHdr, outMailHdr, sendData);		
			struct Socket* wakedSocket = SignalServerCondition(requestMessage.intArg[0],requestMessage.intArg[1],receivedSocketTable[receiveMachineID][receiveMailbox]);
			if(wakedSocket != NULL)  {			//Wake up other's socket
				replyMessage.rplyType = WAITCV;
				replyMessage.status = IWASWAKEDUP;
				outPktHdr.to = wakedSocket->machineID;
				outMailHdr.to = wakedSocket->mailboxNum;

				outMailHdr.length = sizeof(replyMessage);
				sendData = (char*)(&replyMessage);	
				postOffice->Send(outPktHdr, outMailHdr, sendData);	 //Send Back the reply
			}
		
		break;
		case BROADCASTCV:	
			replyMessage.status = BroadcastServerCondition(requestMessage.intArg[0],requestMessage.intArg[1],receivedSocketTable[receiveMachineID][receiveMailbox],true);
			outMailHdr.length = sizeof(replyMessage);
			sendData = (char*)(&replyMessage);	
			postOffice->Send(outPktHdr, outMailHdr, sendData);			
		break;
		
		case CREATEMV:	
			replyMessage.retValue = CreateServerMV(requestMessage.strArg,requestMessage.intArg[0],requestMessage.intArg[1]); //Arg[0]=arraySize Arg[1]=initValue
			if(replyMessage.retValue == -1) {
				replyMessage.status = NOTOK;  
			}
			else {
				replyMessage.status = OK;
			} 
			
			outMailHdr.length = sizeof(replyMessage);
			sendData = (char*)(&replyMessage);		 
			postOffice->Send(outPktHdr, outMailHdr, sendData);	 //Send Back the reply
			
		break;
		
		case DESTROYMV:	
			replyMessage.status = DestroyServerMV(requestMessage.intArg[0]);
			outMailHdr.length = sizeof(replyMessage);
			sendData = (char*)(&replyMessage);	
			postOffice->Send(outPktHdr, outMailHdr, sendData);	 //Send Back the reply
		break;
		
		case GETVALUEMV:	
			replyMessage.retValue = GetValueMV(requestMessage.intArg[0],requestMessage.intArg[1]);
			
			if(replyMessage.retValue == WRONGVALUE) {
				replyMessage.status = NOTOK;  
			}
			else {
				replyMessage.status = OK;
			} 
			
			outMailHdr.length = sizeof(replyMessage);
			sendData = (char*)(&replyMessage);		 
			postOffice->Send(outPktHdr, outMailHdr, sendData);	 //Send Back the reply
					
		break;
		
		case SETVALUEMV:
			replyMessage.status = SetValueMV(requestMessage.intArg[0],requestMessage.intArg[1],requestMessage.intArg[2]);
			outMailHdr.length = sizeof(replyMessage);
			sendData = (char*)(&replyMessage);	
			postOffice->Send(outPktHdr, outMailHdr, sendData);	 //Send Back the reply
		break;
	
		
		}
	
	
	
	}


}
void ServerP2() {
	//Fork another thread with mailbox 1 to handle Message from server
	//printf("\ntangDebug::serverSize is %d", serverSize);
	
	Thread *t = new Thread("ServerMsgHandler");
	t->Fork((VoidFunctionPtr)HandleServerMsg,0);
	
	time_t time_bg = time(NULL);
	struct timeval beginTime;
	if(gettimeofday(&beginTime,NULL) != 0){
			printf("\nServer: Error in gettimeofday()");
			interrupt->Halt();
	}
	
	while(true){	//In this while, only handle request from client
		struct RpcFwdMessage fwdMessage;
		PacketHeader outPktHdr, inPktHdr;
		MailHeader outMailHdr, inMailHdr;
		char recvBuff[MaxMailSize];
		char* sendData;
		
		//receive message from client
		postOffice->Receive(CLIENTMSGBOX, &inPktHdr, &inMailHdr, recvBuff);
		fflush(stdout);
		
		//get valid timestamp
		struct timeval currentTime;
		time_t time_ct = time(NULL);
		int64_t time_stmp;
		if(gettimeofday(&currentTime,NULL) != 0){
			printf("\nServer: Error in gettimeofday()");
			interrupt->Halt();
		}
		time_stmp = ((int64_t)currentTime.tv_sec * 1000000) + (int64_t)currentTime.tv_usec;
		
		if((currentTime.tv_sec - beginTime.tv_sec)<(time_ct-time_bg)){
			printf("\nServer: gettimeofday reset at %d", (int)time_ct);
			time_stmp += ((int64_t)time_ct -(int64_t) time_bg) * 1000000;
		}
		
		//add extra information to form a new message
		fwdMessage.fwdType = REQUESTMSG;
		fwdMessage.timeStamp = time_stmp;
		fwdMessage.clientSock.machineID = inPktHdr.from;
		fwdMessage.clientSock.mailboxNum = inMailHdr.from;
		fwdMessage.serverSock.machineID = inPktHdr.to;
		fwdMessage.serverSock.mailboxNum = inMailHdr.from;
		fwdMessage.reqMessage = *((struct RpcReqMessage *)recvBuff);
		//std::cout<<"\ntangDebug: In Sever mailbox0: receive request from machine "<<inPktHdr.from <<" for type "<<fwdMessage.reqMessage.reqType<<" and strArg "<<fwdMessage.reqMessage.strArg;
		//forward the new message to all servers
		sendData = (char *) (&fwdMessage);
		outMailHdr.from = CLIENTMSGBOX;
		outMailHdr.to = SERVERMSGBOX;
		outMailHdr.length = sizeof(fwdMessage);
		//int serverSize = 3;
		for(int i=0; i<serverSize;i++){
			outPktHdr.to = i;
			int success = postOffice->Send(outPktHdr, outMailHdr, sendData);
			//printf("\ntangDebug: In Server mailbox 0: fwd message to machine %d mailbox %d a client request from client machineid %d", outPktHdr.to, outMailHdr.to, inPktHdr.from);
			if(!success){
				printf("ERROR: Server failure in postOffice->Send()");
				interrupt->Halt();
			}
		}
	}// end of while

}





